package br.ufrn.btest.criterias;

import java.util.ArrayList;
import java.util.List;

public class AllCombinationsCriteria extends Criteria {

	public AllCombinationsCriteria(List<List<String>> parametersInputValues) {
		super(parametersInputValues);
	}

	private static final int MAX_COMBINATIONS_PARAM = 100;

	private <T> long combinations(List<List<T>> list) {
		long count = 1;
		for(List<T> current : list) {
			count = count * current.size();
		}
		return count;
	}

	private <T> List<List<T>> combinate(List<List<T>> uncombinedList) {
		List<List<T>> list = new ArrayList<List<T>>();
		int index[] = new int[uncombinedList.size()];
		
		long totalCombinations = combinations(uncombinedList);
		
		// Initialize index
		for (int i = 0; i < index.length; i++) {
			index[i] = 0;
		}
		// First combination is always valid
		List<T> combination = new ArrayList<T>();
		for (int m = 0; m < index.length; m++) {
			combination.add(uncombinedList.get(m).get(index[m]));
		}
		list.add(combination);
		
		for (int k = 0; k < MAX_COMBINATIONS_PARAM - 1 && k < totalCombinations - 1; k++) {
			combination = new ArrayList<T>();
			boolean found = false;
			// We Use reverse order
			for (int l = index.length - 1; l >= 0 && found == false; l--) {
				int currentListSize = uncombinedList.get(l).size();
				if (index[l] < currentListSize - 1) {
					index[l] = index[l] + 1;
					found = true;
				} else {
					// Overflow
					index[l] = 0;
				}
			}
			for (int m = 0; m < index.length; m++) {
				combination.add(uncombinedList.get(m).get(index[m]));
			}
			list.add(combination);
		}
		return list;
	}
	
	/**
	 * Initialize the list of combinations adding one list for each block of the
	 * first characteristic. After that, it combines the previously created
	 * combinations with the remaining blocks, resulting in a new combinations
	 * list after each iteration.
	 */
	public List<List<String>> getCombinations() {
		List<List<String>> parametersInputValues = getParametersInputValues();
		List<List<String>> combinations = new ArrayList<List<String>>();

		if (!parametersInputValues.isEmpty()) {
			combinations.addAll(combinate(parametersInputValues));
		}

		return combinations;
	}

}
